#include"../include/ttfParser.hpp"
#include<mutex>


namespace LccStuffCollector{
namespace TTFParser{




TTFPARSER_API ttfParser::ttfParser( uint16_t platformID, uint16_t platformSpecificID, uint16_t format):
	platformID(platformID),
	platformSpecificID(platformSpecificID),
	format(format){}


TTFPARSER_API void ttfParser::cover(char* ttfcontent){
	using namespace LccToolbox;// for endianlittle
	reader.formAt(ttfcontent);

	// cmapSubTable choose
	auto& theSubTable=reader.cmap.theSubTable;
	for(uint16_t i=0;i<*(reader.cmap.numberSubtables);++i)
	{	if(reader.cmap.subTable[i].platformID==platformID && reader.cmap.subTable[i].platformSpecificID==platformSpecificID)
		{	theSubTable.cover=reinterpret_cast<cmapSubTableCover*>( ((uint8_t*)reader.cmap.cover) + reader.cmap.subTable[i].offset );
			if(endianlittle()) endianSwap(theSubTable.cover->format);
			if(theSubTable.cover->format==format)
			{	// subTable form
				if(endianlittle()) endianSwap(theSubTable.cover->format);// reset for avoid repeat
				theSubTable.center=&reader;
				theSubTable.form();
				break;// break the loop
			}
			// reset
			if(endianlittle()) endianSwap(theSubTable.cover->format);
			theSubTable.cover=nullptr;
		}
	}
	canuse=theSubTable.cover;
}


TTFPARSER_API void ttfParser::getGlyphIndex( const unicodes& line, glyphIndexes& glyphInds){
	using namespace LccToolbox;// for endianlittle
	if(!canuse||!line.size()) return;

	auto& theSubTable=reader.cmap.theSubTable;// just read, so it's safe for multi thread
	std::mutex maplock;
	// [ main body ]
	auto lamb_getIndexs=[&](size_t m,size_t n)// firstly record in map, then out thread assign to index;
	// {	for(size_t i=0;i<line.size();++i)
	{	for(size_t i=m;i<n;++i)
		{	// check if already record
			// here might in access during other forming [for multi-thread]
			maplock.lock();
			auto it=U2GI.find(line[i]);
			if(it!=U2GI.end()) continue;// already got
			else U2GI[line[i]];
			maplock.unlock();

			// first map
			uint8_t highByte=((uint8_t*)&(line[i]))[endianlittle()?1:0];
			uint8_t lowByte=((uint8_t*)&(line[i]))[endianlittle()?0:1];
			uint32_t rst=0;
			
			switch(format)
			{	case 2:
				{	uint16_t headerIndex=theSubTable.f2.subHeaderKeys[highByte]>>3;
					if(!headerIndex) /*process if lowByte is 0*/break;
					auto& subHeader=theSubTable.f2.subHeaders[headerIndex];
					if(lowByte<subHeader.firstCode) break;
					if(lowByte>=(subHeader.firstCode+subHeader.entryCount)) break;
					rst=((uint32_t)(*(reinterpret_cast<uint16_t*>(((uint8_t*)(&(subHeader.idRangeOffset)+1))+subHeader.idRangeOffset-subHeader.firstCode+lowByte)))+subHeader.idDelta)%65536;
				}break;
				case 4:
				{	uint16_t index_head=0,index_back=*(theSubTable.f4.segCountX2)/2-1,index;
					// for(uint16_t i=0;i<=index_back;++i)
					// 	printf("%u:%u\n",theSubTable.f4.startCode[i],theSubTable.f4.endCode[i]);
					while(1)
					{	if(index_head==index_back) break;
						index=(index_back+index_head)/2;
						if(line[i]<=theSubTable.f4.endCode[index])
						{	if(line[i]>=theSubTable.f4.startCode[index]) break;
							else index_back=index;// smaller
						}
						else index_head=index;// larger
					}
					if(index==*(theSubTable.f4.segCountX2)/2-1) break;
					uint16_t offset=theSubTable.f4.idRangeOffset[index]>>1;// in byte
					if(!offset) rst=((uint32_t)line[i]+theSubTable.f4.idDelta[index])%65536;
					else rst=theSubTable.f4.idRangeOffset[offset+line[i]-theSubTable.f4.startCode[index]+index];
				}break;
			}

			U2GI[line[i]]=rst;
		}
	};

	// thread separate
	size_t jobPerThread=30;
	size_t threadCount=(line.size()-1)/jobPerThread+1;// here size() won't be 0
	block<std::thread> miniThreadPool{threadCount};
	for(size_t i=0;i<threadCount;++i)
		miniThreadPool[i]=std::thread{lamb_getIndexs,i*jobPerThread,(i==threadCount-1)?line.size():((i+1)*jobPerThread)};
	for(size_t i=0;i<threadCount;++i)
		miniThreadPool[i].join();

	glyphInds.alloc(line.size());
	for(size_t i=0;i<line.size();++i)
		glyphInds[i]=U2GI[line[i]];
	// assign to index from map
}


TTFPARSER_API void ttfParser::parse( const glyphIndexes& glyphInds, LccToolbox::block<size_t>& itemInds){
	using namespace LccToolbox;
	if(!canuse||!glyphInds.size()) return;

	std::mutex maplock;
	// [ main body ]
	auto lamb_getItemIndexs=[&](size_t m,size_t n)// firstly record in map, then out thread assign to index;
	{	for(size_t i=m;i<n;++i)
		{	// check if allready loaded
			maplock.lock();
			auto it=GI2RI.find(glyphInds[i]);
			if(it!=GI2RI.end()) continue;
			else GI2RI[glyphInds[i]];
			maplock.unlock();

			// get table
			uint64_t offset=(*reader.head.indexToLocFormat)?(reader.loca.offsets_32[glyphInds[i]]):(reader.loca.offsets_16[glyphInds[i]]<<1);
			offset+=*(reader.tableitems[reader._glyf.index()-1].offset);
			size_t itemIndex=const_cast<LccToolbox::block<glyfItem>&>(glyfItems).size();
			const_cast<LccToolbox::block<glyfItem>&>(glyfItems).grow(1);
			const_cast<LccToolbox::block<glyfItem>&>(glyfItems)[itemIndex].cover=reinterpret_cast<glyfItemCover*>((uint8_t*)(reader.filehead.cover)+offset);
			const_cast<LccToolbox::block<glyfItem>&>(glyfItems)[itemIndex].form();

			GI2RI[glyphInds[i]]=itemIndex;
		}
	};

	// thread separate
	size_t jobPerThread=10;
	size_t threadCount=(glyphInds.size()-1)/jobPerThread+1;// here size() won't be 0
	block<std::thread> miniThreadPool{threadCount};
	for(size_t i=0;i<threadCount;++i)
		miniThreadPool[i]=std::thread{lamb_getItemIndexs,i*jobPerThread,(i==threadCount-1)?glyphInds.size():((i+1)*jobPerThread)};
	for(size_t i=0;i<threadCount;++i)
		miniThreadPool[i].join();

	itemInds.alloc(glyphInds.size());
	for(size_t i=0;i<glyphInds.size();++i)
		itemInds[i]=GI2RI[glyphInds[i]];
}




}// namespace TTFParser
}// namespace LccStuffCollector